package main

/*
安装：
go get github.com/garyburd/redigo/redis
文档：
https://godoc.org/github.com/garyburd/redigo/redis
*/
import (
	"fmt"
	"time"

	"github.com/garyburd/redigo/redis"
)

var (
	redisPool *redis.Pool
)

// 获得redis连接
func conn() redis.Conn {
	conn, err := redis.Dial("tcp", "127.0.0.1:6379")
	if err != nil {
		fmt.Println(err)
		return nil
	}
	// defer c.Close()
	return conn
}

// 初始化连接池
func initConnPool() {
	redisPool = &redis.Pool{
		MaxIdle:     8,                 // 最大空闲连接数
		IdleTimeout: 240 * time.Second, // 最大空闲时间
		MaxActive:   0,                 // 不限制最大连接数
		Dial: func() (redis.Conn, error) {
			return redis.Dial("tcp", "127.0.0.1:6379")
		},
	}
}

/* 测试redis的五大数据类型的操作 */

// string
func testString() {
	initConnPool()
	defer redisPool.Close()
	conn := redisPool.Get()
	res, err := conn.Do("set", "k1", "v1") // 单个设值
	if err != nil {
		fmt.Println("错误！", err)
	} else {
		fmt.Println("正确执行！", res)
	}
	res, err = conn.Do("mset", "k2", "v2", "k3", "v3") // 批量设值
	if err != nil {
		fmt.Println("错误！", err)
	} else {
		fmt.Println("正确执行！", res)
	}
	res, err = conn.Do("mget", "k1", "k2", "k3") // 批量获得
	if err != nil {
		fmt.Println("错误！", err)
	} else {
		// fmt.Println("正确执行！", res)
		// 结果转为[]string 类型如果第二个参数不等于nil就panic异常
		resSlice, errConvert := redis.Strings(res, nil)
		if errConvert != nil {
			fmt.Println("转换出错！", err)
		}
		for _, v := range resSlice {
			fmt.Println(v)
		}
	}
}

func testList() {
	initConnPool()
	defer redisPool.Close()
	conn := redisPool.Get()
	res, err := conn.Do("lpush", "list", "a", "b", "c")
	if err != nil {
		fmt.Println("错误 lpush ！", err)
	} else {
		fmt.Println("正确执行 lpush ！", res)
	}
	// res, err = conn.Do("rpush", "list", "d", "e", "f")
	// if err != nil {
	// 	fmt.Println("错误 rpush ！", err)
	// } else {
	// 	fmt.Println("正确执行 rpush ！", res)
	// }
	res, err = conn.Do("lrange", "list", 0, -1)
	if err != nil {
		fmt.Println("错误 lrange ！", err)
	} else {
		fmt.Println("正确执行 lrange ！", res)// 正确执行 lrange ！ [[99] [98] [97]]
	}
	// 转化为字符串类型
	resSlice, errConvert := redis.Strings(res, nil)
	if errConvert != nil {
		fmt.Println("redis.Strings(res, nil) 出错！", errConvert)
	} else {
		fmt.Println("redis.Strings(res, nil) OK！", resSlice)//redis.Strings(res, nil) OK！ [c b a]
		for _, v := range resSlice {
			fmt.Println(v)
		}
	}
	
	values, errConvertValues := redis.Values(res, nil)
	if errConvertValues != nil {
		fmt.Println("redis.Values(res, nil)出错！", err)
	} else {
		fmt.Println("redis.Values(res, nil) OK!", values)// [[99] [98] [97]]
	}
	
}

func testHash() {
	initConnPool()
	defer redisPool.Close()
	conn := redisPool.Get()
	res, err := conn.Do("hset", "stu", "id", "A100") // 单个设值
	if err != nil {
		fmt.Println("错误！", err)
	} else {
		fmt.Println("正确执行！", res)
	}
	res, err = conn.Do("hmset", "stu", "name", "bart", "age", "16") // 批量设值
	if err != nil {
		fmt.Println("错误！", err)
	} else {
		fmt.Println("正确执行！", res)
	}
	res, err = conn.Do("hget", "stu", "name") // 获得单个值
	if err != nil {
		fmt.Println("错误！", err)
	} else {
		v, _ := redis.String(res, nil)
		fmt.Println("正确执行！", v)
	}
	res, err = conn.Do("hmget", "stu", "id", "name", "age") // 获得多个值
	if err != nil {
		fmt.Println("错误！", err)
	} else {
		v, _ := redis.Strings(res, nil)
		fmt.Println("正确执行！", v) //[A100 bart 16]
	}
	res, err = conn.Do("hgetall", "stu") // 获得所有值
	if err != nil {
		fmt.Println("错误！", err)
	} else {
		// 1) 结果转化为字符数组
		v, _ := redis.Strings(res, nil)
		fmt.Println("正确执行！", v) //[id A100 name bart age 16]
		// 2) 结果转化为字符map
		vMap, _ := redis.StringMap(res, nil)
		fmt.Println("正确执行！", vMap) // map[age:16 id:A100 name:bart]
	}
}

// 测试结构体
func testHashWithStruct() {
	initConnPool()
	defer redisPool.Close()
	c := redisPool.Get()

	// 必须使用 `redis:title` 这种形式的tag标签来映射
	var p1, p2 struct {
		Title  string `redis:"title"`
		Author string `redis:"author"`
		Body   string `redis:"body"`
	}

	p1.Title = "Example"
	p1.Author = "Gary"
	p1.Body = "Hello"

	// AddFlat 顾名思义
	// ... 操作符[]interface{} 的用法，把每个元素使用空格连接
	fmt.Println(redis.Args{}.Add("id1").AddFlat(&p1)...)//id1 title Example author Gary body Hello
	fmt.Println(redis.Args{}.Add("id1").AddFlat(&p1))//[id1 title Example author Gary body Hello]
	if _, err := c.Do("HMSET", redis.Args{}.Add("id1").AddFlat(&p1)...); err != nil {
		fmt.Println(err)
		return
	}

	m := map[string]string{
		"title":  "Example2",
		"author": "Steve",
		"body":   "Map",
	}

	if _, err := c.Do("HMSET", redis.Args{}.Add("id2").AddFlat(m)...); err != nil {
		fmt.Println(err)
		return
	}

	for _, id := range []string{"id1", "id2"} {

		v, err := redis.Values(c.Do("HGETALL", id))
		if err != nil {
			fmt.Println(err)
			return
		}

		if err := redis.ScanStruct(v, &p2); err != nil {
			fmt.Println(err)
			return
		}

		fmt.Printf("%+v\n", p2)
	}
}

// 测试set
func testSet() {
	initConnPool()
	defer redisPool.Close()
	conn := redisPool.Get()

	// 1) 添加一个或者多个（注意是没有顺序的）
	res, err := conn.Do("sadd", "set", "a", "b", "c")
	if err != nil {
		fmt.Println("操作失败！", err)
	}

	// 2) 取出来所有值
	res, err = conn.Do("smembers", "set")
	if err != nil {
		fmt.Println("操作失败！", err)
	}
	resSlice, errConvert := redis.Strings(res, err)
	if errConvert != nil {
		fmt.Println("转换出错！", err)
	}
	for _, v := range resSlice {
		fmt.Println(v)
	}

	// 3) 判断值是否存在
	res, err = conn.Do("sismember", "set", "a")
	if err != nil {
		fmt.Println("操作失败！", err)
	}
	fmt.Println("是否存在：", res)

	// 4) 删除某个值
	res, err = conn.Do("srem", "set", "a")
	if err != nil {
		fmt.Println("操作失败！", err)
	}
	fmt.Println("删除成功：", res)

}

func main() {
	// fmt.Println(conn()) // OK
	// OK
	// initConnPool()
	// fmt.Println(redisPool.Get()) //从池子中获得连接
	// redisPool.Close()            //关闭连接
	// testString()
	testList()
	// testHash()
	// testHashWithStruct()
	// testSet()
}
